package center

import (
	"fmt"
	"sync"

	. "gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/attr"
	. "gddgame.cc/galaxy/common/dsl/game/base"
	. "gddgame.cc/galaxy/common/dsl/game/group"
	. "gddgame.cc/galaxy/common/dsl/game/language"
	. "gddgame.cc/galaxy/common/dsl/game/skill"
	. "gddgame.cc/galaxy/common/dsl/game/stat"
	. "gddgame.cc/galaxy/common/dsl/game/sync"
	. "gddgame.cc/galaxy/common/dsl/game/user"
	"gddgame.cc/galaxy/common/namespaces/cooperate"
	"gddgame.cc/galaxy/common/namespaces/cooperate/structure"
	"gddgame.cc/galaxy/core/models"
	"gddgame.cc/galaxy/project/breeder/breeder/model"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/uuid"
)

type ThresholdHandle func(mapInstance cooperate.MapProxy, object cooperate.PropsInstance, params []interface{})
type ThresholdHandleWrap func(mapInstance cooperate.MapProxy, dataType structure.PublicDataType, id interface{}, params []interface{}) cooperate.PropsInstance

type UpdateReceiveHandle func(context def.Context, receive *model.Receive, number int, user *model.User) bool

type DownUnitHandle func(context def.Context, user *model.User, userUnit *model.UserUnit) (err error)

type GetUserByAccountHandle func(context def.Context, accountId int) (*model.User, error)

type ValidCreateBuildHandle func(context def.Context, buildId int, user *model.User, account cooperate.PropsInstance, creater cooperate.PropsInstance, needConsume bool) error

type ValidAndUseUnitPropHandle func(context def.Context, userUnitId string, propId int, needConsume bool) error

type GetUnitWithUserHandle func(context def.Context, userUnitId string, user *model.User) (*model.UserUnit, error)

type Center struct {
	env *Environment
	*cooperate.LogicWrap

	*GroupData

	*StatData

	*BaseData

	*UserData

	*AttrData

	*SkillData

	*LanguageData

	*SyncData

	*model.ModelData

	MapNodes map[string][]string
	NodeMaps map[string][]string

	OperationLevels map[structure.PublicDataType]map[string][]*model.Operation
	OperationSelf   []*model.Operation

	TargetIdMap  map[structure.PublicDataType]map[string]int
	TargetKeyMap map[structure.PublicDataType]map[int]string

	SyncDefineList map[structure.PublicDataType]map[string][]string

	BindOperationMap map[structure.PublicDataType]map[int]map[string]model.BindOperationInfo

	StatMap        map[byte][][][][][]*Trigger
	StatCacheMutex sync.RWMutex
	StatMapCache   map[string]map[string]*Trigger
	StatTypeMap    map[structure.PublicDataType][]utils.StatConfig

	ThresholdEvents map[byte]ThresholdHandle

	HealthId int

	ShowMap map[int]int

	UpdateReceive       UpdateReceiveHandle
	GetUserByAccount    GetUserByAccountHandle
	DownUnit            DownUnitHandle
	ValidCreateBuild    ValidCreateBuildHandle
	ValidAndUseUnitProp ValidAndUseUnitPropHandle
	GetUnitWithUser     GetUnitWithUserHandle
}

func NewCenter(env *Environment) *Center {

	return &Center{
		env: env,

		TargetIdMap:  map[structure.PublicDataType]map[string]int{},
		TargetKeyMap: map[structure.PublicDataType]map[int]string{},

		BindOperationMap: map[structure.PublicDataType]map[int]map[string]model.BindOperationInfo{},
		ThresholdEvents:  map[byte]ThresholdHandle{},
	}
}

func (center *Center) Compute() {
	center.initLanguage()
	center.MapNodes = map[string][]string{}
	center.NodeMaps = map[string][]string{}
	for _, mapNo := range center.MapNos {
		center.MapNodes[mapNo.Id] = mapNo.Nodes
		for _, node := range mapNo.Nodes {
			ll, ok := center.NodeMaps[node]
			if !ok {
				ll = []string{}
			}
			ll = append(ll, mapNo.Id)
			center.NodeMaps[node] = ll
		}
	}
	// 根据类型分组
	center.OperationLevels = map[structure.PublicDataType]map[string][]*model.Operation{}
	center.OperationSelf = []*model.Operation{}
	for index := range center.SettingOperations {
		operation := center.SettingOperations[index]
		targets := operation.Target

		if targets != nil {
			for _, target := range targets {
				pMap, ok := center.OperationLevels[target.Type]
				if !ok {
					pMap = map[string][]*model.Operation{}
					center.OperationLevels[target.Type] = pMap
				}
				if len(target.Ids) == 0 {
					target.Ids = append(target.Ids, 0)
				}
				for _, id := range target.Ids {
					var key string
					if id == 0 {
						key = ""
					} else {
						key = center.TargetKeyMap[target.Type][id]
					}
					kMap, ok := pMap[key]
					if !ok {
						kMap = []*model.Operation{}
					}
					pMap[key] = append(kMap, center.OperationList[index])
				}
			}
		}
		if operation.UnitType == model.SelfUnitOperationType {
			center.OperationSelf = append(center.OperationSelf, center.OperationList[index])
		}

	}

	center.StatTypeMap = map[structure.PublicDataType][]utils.StatConfig{}
	center.StatMap = map[byte][][][][][]*Trigger{}
	center.StatMapCache = map[string]map[string]*Trigger{}

	tmpStatTypeMap := map[structure.PublicDataType]map[string]struct{}{}
	for _, settingStat := range center.Stats {
		for index := range settingStat.TriggerConfig {
			trigger := &settingStat.TriggerConfig[index]
			l, ok := center.StatMap[trigger.Event]
			if !ok {
				l = [][][][][]*Trigger{}
			}
			if trigger.Target == nil || len(trigger.Target) < 2 {
				trigger.Target = []int{0, 0}
			}
			if trigger.From == nil || len(trigger.From) < 2 {
				trigger.From = []int{0, 0}
			}
			if len(l) <= trigger.Target[0] {
				l = append(l, make([][][][][]*Trigger, trigger.Target[0]-len(l)+1)...)
			}
			ll := l[trigger.Target[0]]
			if len(ll) <= trigger.Target[1] {
				ll = append(ll, make([][][][]*Trigger, trigger.Target[1]-len(ll)+1)...)
			}
			lll := ll[trigger.Target[1]]
			if len(lll) <= trigger.From[0] {
				lll = append(lll, make([][][]*Trigger, trigger.From[0]-len(lll)+1)...)
			}
			llll := lll[trigger.From[0]]
			if len(llll) <= trigger.From[1] {
				llll = append(llll, make([][]*Trigger, trigger.From[1]-len(llll)+1)...)
			}
			lllll := llll[trigger.From[1]]
			lllll = append(lllll, trigger)
			llll[trigger.From[1]] = lllll
			lll[trigger.From[0]] = llll
			ll[trigger.Target[1]] = lll
			l[trigger.Target[0]] = ll
			center.StatMap[trigger.Event] = l

			t := trigger.From[0]
			if trigger.Direction {
				t = trigger.Target[0]
			}
			if trigger.Inherit != "" {
				// 获取该继承实际的类型
				depend := center.DefaultDepend(structure.PublicDataType(t), trigger.Inherit)
				t = int(depend.Be.BeDependType)
			}
			tt := structure.PublicDataType(t)
			d, ok := tmpStatTypeMap[tt]
			if !ok {
				d = map[string]struct{}{}
				tmpStatTypeMap[tt] = d
			}
		}
	}
	for dataType, keyMap := range tmpStatTypeMap {
		l := []utils.StatConfig{}
		for key := range keyMap {
			l = append(l, center.GetStatConfigByKey(key)...)
		}
		center.StatTypeMap[dataType] = l
	}

	center.ShowMap = center.AttrData.ListToMap(model.ShowAttrList)
	center.HealthId = center.AttrData.KeyToId("allHealth")
}

func (center *Center) initLanguage() {
}

func (center *Center) GetUserFromOpen(open *models.UserOpen) UserBase {
	var u model.User
	if open.Id == "" {
		openId := uuid.RandomString()
		u = model.User{OpenId: openId}
	} else {
		_ = center.FindUser(open.Id, &u)
	}
	return &u
}

func (center *Center) BindDataType(dataType structure.PublicDataType, id int, key string) {
	idMap, ok := center.TargetIdMap[dataType]
	if !ok {
		idMap = map[string]int{}
		center.TargetIdMap[dataType] = idMap
	}
	idMap[key] = id
	keyMap, ok := center.TargetKeyMap[dataType]
	if !ok {
		keyMap = map[int]string{}
		center.TargetKeyMap[dataType] = keyMap
	}
	keyMap[id] = key
}

func (center *Center) GetConfig(key string, value interface{}) {
	center.env.Config(key, value)
}

func (center *Center) BindOperation(dataType structure.PublicDataType, id int, level int, relation string, require model.CompareDataRelationTextShowSlice, consume model.DataRelationReachShowSlice) {
	if require == nil {
		return
	}
	dr, ok := center.BindOperationMap[dataType]
	if !ok {
		dr = map[int]map[string]model.BindOperationInfo{}
		center.BindOperationMap[dataType] = dr
	}
	ir, ok := dr[id]
	if !ok {
		ir = map[string]model.BindOperationInfo{}
		dr[id] = ir
	}
	if level >= 0 {
		relation += "/" + utils.ToString(level)
	}
	ir[relation] = model.BindOperationInfo{
		Require: require,
		Consume: consume,
	}
}
func (center *Center) SelectBindOperation(relationSelect *model.RelationSelect, id int, dataType structure.PublicDataType, level int) (model.CompareDataRelationTextShowSlice, model.DataRelationReachShowSlice) {
	if relationSelect == nil {
		return nil, nil
	}
	dr, ok := center.BindOperationMap[dataType]
	if !ok {
		return nil, nil
	}
	ir, ok := dr[id]
	if !ok {
		return nil, nil
	}
	relation := relationSelect.Relation
	if relation != "" {
		return nil, nil
	}
	if relationSelect.Level {
		relation += "/" + utils.ToString(level)
	}
	fr, ok := ir[relation]
	if !ok {
		return nil, nil
	}
	return fr.Require, fr.Consume
}

func (center *Center) BindThresholdEvent(event byte, handle ThresholdHandle, handleWrap ThresholdHandleWrap) {
	center.ThresholdEvents[event] = handle
	center.OnSignal(event, func(mapInstance cooperate.MapProxy, dataType structure.PublicDataType, id interface{}, params []interface{}) {
		object := handleWrap(mapInstance, dataType, id, params)
		handle(mapInstance, object, params)
	})
}

func (center *Center) GetStatData(object cooperate.PropsInstance) map[string]int32 {
	km, ok := center.StatTypeMap[object.GetDataType()]
	if !ok {
		return nil
	}
	data := center.StatValues(object, km)
	return data[utils.AllCycle]
}

func (center *Center) UpdateStatNotice(mapInstance cooperate.MapProxy, instance cooperate.PropsInstance, key string, old int, value int) {
	//fmt.Println("update stat", key, old, value)
	setting, _ := center.GetStatByKey(key)
	if setting == nil {
		return
	}
	//fmt.Println("update stat setting", setting)
	var thresholdRef *Threshold
	switch setting.Type {
	case utils.MinStat:
		if old > 0 && value > old {
			return
		}
		start := false
		end := -1
		for index, threshold := range setting.ThresholdConfig {
			if threshold.Value < value {
				break
			}
			if threshold.Value < old {
				start = true
			}
			if start && threshold.Value >= value {
				end = index
			}
		}
		if end >= 0 {
			thresholdRef = &setting.ThresholdConfig[end]
			goto Trigger
		}
	case utils.MaxStat:
		fallthrough
	case utils.CountStat:
		if old >= value {
			return
		}
		start := false
		end := -1
		for index, threshold := range setting.ThresholdConfig {
			if threshold.Value > value {
				break
			}
			if threshold.Value > old {
				start = true
			}
			if start && threshold.Value <= value {
				end = index
			}
		}
		if end >= 0 {
			thresholdRef = &setting.ThresholdConfig[end]
			goto Trigger
		}
	default:
		return
	}
Trigger:

	//fmt.Println("update stat trigger", thresholdRef)
	if thresholdRef != nil {
		if handler, ok := center.ThresholdEvents[thresholdRef.Event]; ok {
			if thresholdRef.Inherit != "" {
				tt := instance.Inherit(thresholdRef.Inherit)
				instance = mapInstance.GetDetailCache(mapInstance, tt)
				if instance == nil {
					return
				}
			}
			//fmt.Println("update stat trigger", thresholdRef.Params)
			handler(mapInstance, instance, thresholdRef.Params)
		}
	}
}

func (center *Center) TriggerStat(event byte, mapInstance cooperate.MapProxy, from cooperate.PropsInstance, target cooperate.PropsInstance, value int32) {
	if target == nil {
		return
	}
	tl := center.TriggerCache(event, from, target)
	if tl == nil {
		return
	}
	result := map[string][]cooperate.OperateData{}
	for _, trigger := range tl {
		t := from
		if trigger.Direction {
			t = target
		}
		if t == nil {
			continue
		}
		if trigger.Inherit != "" {
			tt := t.Inherit(trigger.Inherit)
			t = mapInstance.GetDetailCache(mapInstance, tt)
			if t == nil {
				continue
			}
		}
		switch trigger.Effect {
		case utils.MultipleEffect:
			value = value * trigger.Value
		case utils.FixedEffect:
			value = trigger.Value
		case utils.DynamicEffect:
			value = int32(t.AttrWithId(int(trigger.Value)))
		}

		update := target.Stat().Stat(trigger.Stat.Configs, t, value, trigger.Effect == utils.ResetEffect, trigger.Stat.AutoNotice || (trigger.Stat.ThresholdConfig != nil && len(trigger.Stat.ThresholdConfig) > 0))
		item := utils.ToString(t.Data().GetItem())
		b, ok := result[item]
		if !ok {
			b = []cooperate.OperateData{}
		}

		result[item] = append(b, update...)
	}
	//fmt.Println("trigger", result)
	for d, op := range result {
		mapInstance.PushOperate(cooperate.OperateContent{
			MapNoId:  mapInstance.GetMapNoId(),
			Target:   structure.NewBaseDataString(d),
			Operates: op,
		}, 0)
	}

}

func (center *Center) TriggerCache(event byte, from cooperate.PropsInstance, target cooperate.PropsInstance) (tl map[string]*Trigger) {
	var d1 structure.PublicDataType
	var d2 structure.PublicDataType
	var id1 int
	var id2 int
	if from != nil {
		dt := from.GetDataType()
		key := from.ToString(cooperate.Key)
		id2 = center.TargetIdMap[dt][key]
		d2 = dt
	}
	dt := target.GetDataType()
	key := target.ToString(cooperate.Key)
	id1 = center.TargetIdMap[dt][key]
	d1 = dt
	cacheKey := fmt.Sprintf("%s%s:%s%s", d1, id1, d2, id2)
	center.StatCacheMutex.RLock()
	tl, ok := center.StatMapCache[cacheKey]
	center.StatCacheMutex.RUnlock()
	if ok {
		return
	}

	m, ok := center.StatMap[event]
	if !ok {
		return
	}
	if m == nil || len(m) == 0 {
		return
	}
	var mm [][][][]*Trigger
	if len(m) > 0 {
		if m[0] != nil && len(m[0]) > 0 {
			mm = append(mm, m[0][0])
		}
	}
	if len(m) > int(d1) {
		m1 := m[d1]
		if m1[0] != nil && len(m1[0]) > 0 {
			mm = append(mm, m1[0])
		}

		if len(m1) > id1 {
			if m1[id1] != nil && len(m1[id1]) > 0 {
				mm = append(mm, m1[id1])
			}
		}
	}
	if mm == nil || len(mm) == 0 {
		return
	}
	var mmm []*Trigger
	for _, mt := range mm {
		if len(mt) > 0 {
			if mt[0] != nil && len(mt[0]) > 0 {
				mmm = append(mmm, mt[0][0]...)
			}
		}
		if from == nil {
			continue
		}
		if len(mt) > int(d2) {
			m1 := mt[d2]
			if m1[0] != nil && len(m1[0]) > 0 {
				mmm = append(mmm, m1[0]...)
			}

			if len(m1) > id2 {
				if m1[id2] != nil && len(m1[id2]) > 0 {
					mmm = append(mmm, m1[id2]...)
				}
			}
		}
	}
	if mmm == nil || len(mmm) == 0 {
		return
	}
	tl = map[string]*Trigger{}
	for _, trigger := range mmm {
		oldTrigger, ok := tl[trigger.Stat.Key]
		if ok {
			if oldTrigger.Priority > trigger.Priority {
				continue
			}
		}
		tl[trigger.Stat.Key] = trigger
	}
	center.StatCacheMutex.Lock()
	center.StatMapCache[cacheKey] = tl
	center.StatCacheMutex.Unlock()
	//fmt.Println("cache trigger", tl)
	return
}

func (center *Center) OnReceiveNotice(mapInstance cooperate.MapProxy, accountId int, item *model.Item) {
	if accountId == 0 {
		return
	}
	context := center.env.Context()
	user, _ := center.GetUserByAccount(context, accountId)
	if user == nil {
		return
	}
	center.UpdateReceive(context, item.ReceiveSlice, 1, user)
	show, _ := item.Receive.MarshalMsg(nil)
	mapInstance.PushNotice(mapInstance.GetMapNoId(), accountId, PickUpReceiveNoticeEvent, show)
}

func (center *Center) OnUnitDown(mapInstance cooperate.MapProxy, accountId int, userUnitId string) {
	fmt.Println("on unit downn", accountId, userUnitId)
	if accountId == 0 {
		return
	}
	fmt.Println("on unit downn 1", accountId, userUnitId)
	context := center.env.Context()
	user, err := center.GetUserByAccount(context, accountId)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("on unit downn 2", accountId, userUnitId)
	userUnit, err := center.GetUnitWithUser(context, userUnitId, user)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("on unit downn3", accountId, userUnitId)
	if err := center.DownUnit(context, user, userUnit); err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("on unit downn4", accountId, userUnitId)
	mapInstance.PushNotice(mapInstance.GetMapNoId(), accountId, UnitChangeNoticeEvent, nil)
}

func (center *Center) OnNoticeError(mapInstance cooperate.MapProxy, accountId int, err error) {
	mapInstance.PushNotice(mapInstance.GetMapNoId(), accountId, ErrorNoticeEvent, utils.ToBytes(err.Error()))
}

func (center *Center) OnCreateBuild(accountId int, account cooperate.PropsInstance, buildId int, data cooperate.PropsInstance) error {
	if accountId == 0 {
		return nil
	}
	context := center.env.Context()
	user, _ := center.GetUserByAccount(context, accountId)
	if user == nil {
		return nil
	}
	if err := center.ValidCreateBuild(context, buildId, user, account, data, true); err != nil {
		return err
	}
	return nil
}

func (center *Center) OnUseDirect(mapInstance cooperate.MapProxy, accountId int, selfIdentity int, data structure.Data, direct *model.Direct) error {
	// 验证道具数量
	var err error
	context := center.env.Context()
	prop, ok := center.PropWithDirectMap[direct.Id]
	if !ok {
		goto Return
	}
	if errTmp := center.ValidAndUseUnitProp(context, utils.ToString(data.GetId()), prop.Id, true); errTmp != nil {
		err = center.LanguageError("DIRECT_NOT_PROP")
		goto Return
	}
	// 更新部队
	// todo 依赖
	mapInstance.OnQuery(accountId, selfIdentity, 1, []interface{}{UnitType, utils.ToString(data.GetId()), model.PropAbilitySource})

Return:
	return err
}
